rm(list = ls(all = TRUE))
setwd("C:\\Users\\lenovo\\Desktop\\R\\神经网络_R语言实现")
#5.1 使用神经网络进行数据拟合
#数据拟合是建立一组事先收集的点最匹配的曲线或数学函数的过程
#曲线拟合涉及插值和平滑
#插值时需要精确的数据点
#在平滑时构建逼近数据的平坦函数

#从数据拟合中获得的近似曲线有助于显示数据、
#预测没有数据可用的函数的值
#并总结两个或多个变量之间的关系

#数据拟合是在一组输入上训练神经网络以产生一组相关联的目标输出的过程
#一旦神经网络拟合数据，就形成了输入-输出关系的泛化，
#并可用于生成未经过训练的输入对应的输出

#以自带的数据为例，预测汽车的油耗

#加载包
library(neuralnet)
#使用反向传播、有或无权重回溯的弹性反向传播（RPROP）
#或修正的全局收敛版本（GRPROP）
#该函数允许通过自定义误差和激活函数进行灵活设置
library(ISLR)

#加载数据
data = Auto
View(data)

#5.1.1 探索性分析
#先了解数据是如何分布的
plot(data$weight, data$mpg, pch = data$origin, cex = 2)
#可见，mpg越大，燃油值消耗越低
#美国生产的汽车更重

#按行填充的2行*2列
#若要按列填充，mfcol = c(nrows.ncols)
par(mfrow = c(2,2))
plot(data$cylinders, data$mpg, pch = data$origin, cex = 1)
plot(data$displacement, data$mpg, pch = data$origin, cex = 1)
plot(data$horsepower, data$mpg, pch = data$origin, cex = 1)
plot(data$acceleration, data$mpg, pch = data$origin, cex = 1)
#马力较高的车辆的油耗较高
#排量较高的车辆具有较高的油耗
#美国生产的汽车具有更高的马力和排量值

#具有较高加速度的汽车具有较低的燃料消耗，这是因为它具有较轻的重量
#通常情况下，重型车辆的加速度较慢


#5.1.2 神经网络模型

#一般采用最小-最大标准化
#此处采用Z分数标准化
#Z分数表示观测点或数据的值超过所观测或测量的平均值的数值
#是标准差的几倍
#1为行，2为列
mean_data <- apply(data[1:6], 2, mean)
sd_data <- apply(data[1:6], 2, sd)

data_scaled <- as.data.frame(scale(data[1:6], center = mean_data,
                                  scale = sd_data))
head(data_scaled, n = 20)

#分为训练集与测试集
index = sample(1:nrow(data), round(0.70*nrow(data)))
train_data <- as.data.frame(data_scaled[index,])
test_data <- as.data.frame(data_scaled[-index,])
n = names(data_scaled)
f = as.formula(paste("mpg ~", paste(n[!n %in% "mpg"], collapse = " + ")))

#linear.output用于指定想要进行回归（TRUE）还是分类（FALSE）
#默认是使用基于无加权回溯的弹性反向传播
net = neuralnet(f, data =  train_data, hidden = 3, linear.output = TRUE)
summary(net)
plot(net)
#1代表在每一步添加的偏差
net$result.matrix

predict_net_test <- compute(net, test_data[,2:6])
MSE.net <- sum((test_data$mpg - predict_net_test$net.result)^2)/nrow(test_data)
MSE.net

Lm_Mod <- lm(mpg~. , data = train_data)
summary(Lm_Mod)
predict_lm <- predict(Lm_Mod, test_data)
MSE.lm <- sum((predict_lm - test_data$mpg)^2)/nrow(test_data)
MSE.lm

par(mfrow = c(1,2))
plot(test_data$mpg, predict_net_test$net.result, col = "red",
     main = "Real vs predicted for neural network", 
     pch = 18, cex = 4)
abline(0,1,lwd = 5)
plot(test_data$mpg, predict_lm, col = "red",
     main = "Real vs predicted for linear regression",
     pch = 18, cex =4)
abline(0,1,lwd = 5)



#5.2 使用神经网络对乳腺癌进行分类
#install.packages("mlbench")

#添加包melbench包含一系列人工的和现实世界的机器学习基准测试问题
library(mlbench)
library(neuralnet)

data(BreastCancer)

#5.2.1 探索性分析
summary(BreastCancer)

#用is.na识别缺失值
#unique用于删除重复项
#unlist将该列表变为一个向量
mvindx = unique(unlist(lapply(BreastCancer, 
                              function(x) which (is.na(x)))))
#na.omit丢弃任何存在缺失值的行并永远删除它
#na.exclude丢弃存在缺失值的行，但记录他们的位置
#例如，当进行预测时，最终会得到一个长度为原始响应变量长度的向量
data_cleaned <- na.omit(BreastCancer)
summary(data_cleaned)

#用boxplot识别异常值
boxplot(data_cleaned[,2:10])
hist(as.numeric(data_cleaned$Mitoses))

par(mfrow=c(3, 3))
hist(as.numeric(data_cleaned$Cl.thickness))
hist(as.numeric(data_cleaned$Cell.size))
hist(as.numeric(data_cleaned$Cell.shape))
hist(as.numeric(data_cleaned$Marg.adhesion))
hist(as.numeric(data_cleaned$Epith.c.size))
hist(as.numeric(data_cleaned$Bare.nuclei))
hist(as.numeric(data_cleaned$Bl.cromatin))
hist(as.numeric(data_cleaned$Normal.nucleoli))
hist(as.numeric(data_cleaned$Mitoses))
#从直方图可以看出，一些变量存在异常值


#5.2.2 神经网络模型
str(data_scaled)
#展示对象的内部结构

#识别因子类型的变量，然后将它们转换为数值类型
input <- data_cleaned[,2:10]
indx<- sapply(input, is.factor)
input <- as.data.frame(lapply(input, function(x) as.numeric(as.character(x))))

#标准化。特征缩放
max_data <- apply(input, 2, max)
min_data <- apply(input, 2, min)
input_scaled <- as.data.frame(scale(input,center = min_data, scale = max_data - min_data))
View(input_scaled)

Cancer<-data_cleaned$Class
Cancer<-as.data.frame(Cancer)
#函数model.matrix通过将因子扩展得到一组虚拟变量，
#并以类似的方式扩展
Cancer<-with(Cancer, data.frame(model.matrix(~Cancer+0)))

final_data<-as.data.frame(cbind(input_scaled,Cancer))


#5.2.3 网络训练阶段

#数据分类
index = sample(1:nrow(final_data),round(0.70*nrow(final_data)))
train_data <- as.data.frame(final_data[index,])
test_data <- as.data.frame(final_data[-index,])

#建立提交给测试网络的公式
n = names(final_data[1:9])
f = as.formula(paste("Cancerbenign + Cancermalignant ~", paste(n, collapse = " + ")))

#训练网络
net = neuralnet(f,data=train_data,hidden=5,linear.output=FALSE)
plot(net)


#5.2.4 测试神经网络
predict_net_test <- compute(net,test_data[,1:9])
#将输出结果四舍五入到整数，以进行比较
predict_result<-round(predict_net_test$net.result, digits = 0)
#将虚拟变量重建
net.prediction = c("benign", "malignant")[apply(predict_result, 1, which.max)]
#混淆矩阵
predict.table = table(data_cleaned$Class[-index],net.prediction)
predict.table

#install.packages("gmodels")
library(gmodels)
CrossTable(x = data_cleaned$Class[-index], y = net.prediction,
           prob.chisq = FALSE)


#5.3 神经网络训练中的早期停止

#epoch是度量前向传播训练以及权重和偏差的反向传播更新的每次往返
#一旦达到收敛(最小误差项)或者经过预设的迭代次数，
#训练的往返就必须停止

#早期终止是用于处理模型过拟合的技术

#利用训练数据，计算梯度并更新网络权重和偏差
#第二组数据，即测试数据与验证数据，用于验证模型过拟合
#如果验证过程中的误差达到了指定的数（nnet.abstol/reltol），
#则停止训练，模型将使用该点的权重和偏差

#早期停止的神经网络集成泛化误差，
#这与由传统算法训练的最佳结构的单个神经网络相当
#单个的神经网络需要一个复杂和完美的调整，以达到这种泛化

#5.4 避免模型中的过拟合

#当算法在某些训练数据集中表现得太好时，称为与该特定数据集过于一致
#当测试数据和训练数据非常不同时，会导致输出值的高度不同
#这种过高的估计方差被称为过拟合（over-fitting）

#处理过拟合的方法很多。首先是正则化。
#正则化引入一个成本项来影响激活函数。
#它试图在目标函数中引入更多特征来改变大部分系数，
#试图将许多变量的系数推到零，并降低成本项。

#（1）、L1或LASSO正则化
#有一个惩罚项，它使用绝对权重的总和，以便优化权重来减少过拟合
#最小绝对收缩和选择算子
#（Least Absolute Shrinkage and Selection Operater，LASSO）
#引入了惩罚权重，能将网络权重缩小到零

#（2）、L2或岭（ridge）正则化
#与L1类似，但惩罚项基于权重值的平方和而不是绝对权重的总和
#权重越大，受到的惩罚越多

#（3）、最大范数约束
#另一种正则化技术，
#可对每个神经元的强度执行绝对上限，并且受到约束，
#投影梯度下降法不能修改权重

#在这里，参数向量不能失控（即使学习率太高），因为权重的更新总是有界的


#（4）、神经网络中的dropout
#这是另一种过拟合的预防技术
#dropout总是保持一个神经元以某种概率p（一个超参数）活跃
#或者否则将其设置为0

#一些神经元在训练时可能不存在，因此成为dropout

#网络不会受此影响，甚至在缺乏某些信息的情况下更为准确

#这可防止网络过度依赖于神经元的任何一个（或任何小组合）

#5.5 神经网络的泛化

#泛化旨在拟合训练数据，它是我们在神经网络模型进行的训练的延伸。
#它试图最小化模型在训练数据上的平方误差总和（例如使用最小二乘法）
#并降低模型的复杂性

#泛化常用的方法：
#训练的早期停止
#用不同训练数据重新训练神经网络使用随机抽样、分层抽样或任何良好的目标数据组合
#训练多个神经网络并取输出的平均值


#5.6 神经网络模型中数据的缩放

#数据的缩放或标准化是以标准格式制作模型数据的过程，以便训练得到改进
#从而变得准确和快速

#常用的标准化方法：
#Z分数标准化
#该方法对异常值很敏感
#最小-最大标准化
#对异常值特别敏感
#中位数和MAD（Median Absolute Deviation）中位数绝对偏差
#对于异常值或者分布尾端的值不敏感
#但是不保留数据的分布，也不会将分数转换为常见的数值范围


#5.7 集成神经网络来预测

#另一种正则化方法涉及组合神经网络模型和平均模型的结果
#由此产生的模型是最稳定的
#神经网络集成是使用一组神经网络模型，
#通过对各个模型的结果进行平均来做出决定

#集成技术是改善泛化的一种简单方法，尤其是受噪声数据或小数据影响时
#训练多个神经网络并平均他们的输出

